/*
 * assignment4Part2.cpp
 *
 *  Created on: 16-Oct-2013
 *      Author: pratibind_jha
 */

#include "texture.h"
#include <stdio.h>
#include <math.h>
#include <strings.h>
#include <ply.h>

/* user's vertex and face definitions for a polygonal object */

typedef struct Vertex {
  float x,y,z;
  float nx,ny,nz;
  void *other_props;       /* other properties */
} Vertex;

typedef struct Face {
  unsigned char nverts;    /* number of vertex indices in list */
  int *verts;              /* vertex index list */
  void *other_props;       /* other properties */
} Face;

static int nverts,nfaces;
static Vertex **vlist;
static Face **flist;

static int flip_sign = 0;       /* flip the sign of the normals? */

/******************************************************************************
Allocate some memory.

Entry:
  size  - amount of memory requested (in bytes)
  lnum  - line number from which memory was requested
  fname - file name from which memory was requested
******************************************************************************/

static char *my_alloc(int size, int lnum, char *fname)
{
  char *ptr;

  ptr = (char *) malloc (size);

  if (ptr == 0) {
    fprintf(stderr, "Memory allocation bombed on line %d in %s\n", lnum, fname);
  }

  return (ptr);
}


/******************************************************************************
Given a file pointer, get ready to read PLY data from the file.

Entry:
  fp - the given file pointer

Exit:
  nelems     - number of elements in object
  elem_names - list of element names
  returns a pointer to a PlyFile, used to refer to this file, or NULL if error
******************************************************************************/

PlyFile *read_ply(FILE *fp)
{
  PlyFile *ply;
  int num_elems;
  char **elem_names;

  ply = ply_read (fp, &num_elems, &elem_names);

  return (ply);
}

/******************************************************************************
Read in the PLY file from standard in.
******************************************************************************/

void read_file()
{
  int i,j;
  int elem_count;
  char *elem_name;

  /*** Read in the original PLY object ***/

  in_ply = read_ply (stdin);

  for (i = 0; i < in_ply->num_elem_types; i++) {

    /* prepare to read the i'th list of elements */
    elem_name = setup_element_read_ply (in_ply, i, &elem_count);

    if (equal_strings ("vertex", elem_name)) {

      /* create a vertex list to hold all the vertices */
      vlist = (Vertex **) malloc (sizeof (Vertex *) * elem_count);
      nverts = elem_count;

      /* set up for getting vertex elements */

      setup_property_ply (in_ply, &vert_props[0]);
      setup_property_ply (in_ply, &vert_props[1]);
      setup_property_ply (in_ply, &vert_props[2]);

      for (j = 0; j < in_ply->elems[i]->nprops; j++) {
	PlyProperty *prop;
	prop = in_ply->elems[i]->props[j];
	if (equal_strings ("nx", prop->name))
	  setup_property_ply (in_ply, &vert_props[3]);
	if (equal_strings ("ny", prop->name))
	  setup_property_ply (in_ply, &vert_props[4]);
	if (equal_strings ("nz", prop->name))
	  setup_property_ply (in_ply, &vert_props[5]);
      }

      vert_other = get_other_properties_ply (in_ply,
					     offsetof(Vertex,other_props));

      /* grab all the vertex elements */
      for (j = 0; j < elem_count; j++) {
        vlist[j] = (Vertex *) malloc (sizeof (Vertex));
        get_element_ply (in_ply, (void *) vlist[j]);
      }
    }
    else if (equal_strings ("face", elem_name)) {

      /* create a list to hold all the face elements */
      flist = (Face **) malloc (sizeof (Face *) * elem_count);
      nfaces = elem_count;

      /* set up for getting face elements */

      setup_property_ply (in_ply, &face_props[0]);
      face_other = get_other_properties_ply (in_ply,
					     offsetof(Face,other_props));

      /* grab all the face elements */
      for (j = 0; j < elem_count; j++) {
        flist[j] = (Face *) malloc (sizeof (Face));
        get_element_ply (in_ply, (void *) flist[j]);
      }
    }
    else
      get_other_element_ply (in_ply);
  }

  close_ply (in_ply);
}


/******************************************************************************
Compute normals at the vertices.
******************************************************************************/

void compute_normals()
{
  int i,j;
  Face *face;
  Vertex *vert;
  int *verts;
  float x,y,z;
  float x0,y0,z0;
  float x1,y1,z1;
  float len;
  float recip;

  /* zero out all normal information at vertices */

  for (i = 0; i < nverts; i++) {
    vlist[i]->nx = 0;
    vlist[i]->ny = 0;
    vlist[i]->nz = 0;
  }

  /* find normal of each face and add it to each vertex adjacent to the face */

  for (i = 0; i < nfaces; i++) {

    face = flist[i];
    verts = face->verts;

    /* determine vectors parallel to two edges of face */

    x0 = vlist[verts[face->nverts-1]]->x - vlist[verts[0]]->x;
    y0 = vlist[verts[face->nverts-1]]->y - vlist[verts[0]]->y;
    z0 = vlist[verts[face->nverts-1]]->z - vlist[verts[0]]->z;

    x1 = vlist[verts[1]]->x - vlist[verts[0]]->x;
    y1 = vlist[verts[1]]->y - vlist[verts[0]]->y;
    z1 = vlist[verts[1]]->z - vlist[verts[0]]->z;

    /* find cross-product between these vectors */
    x = y0 * z1 - z0 * y1;
    y = z0 * x1 - x0 * z1;
    z = x0 * y1 - y0 * x1;

    /* normalize this vector */
    len = x*x + y*y + z*z;
    if (len == 0) {
      x = y = z = 0;
    }
    else {
      recip = 1 / sqrt (len);
      x *= recip;
      y *= recip;
      z *= recip;
    }

    /* add this normal to each vertex that is adjacent to face */
    for (j = 0; j < face->nverts; j++) {
      vlist[verts[j]]->nx += x;
      vlist[verts[j]]->ny += y;
      vlist[verts[j]]->nz += z;
    }
  }

  /* normalize all the normals at the vertices */

  for (i = 0; i < nverts; i++) {
    vert = vlist[i];
    len = vert->nx * vert->nx + vert->ny * vert->ny + vert->nz * vert->nz;
    if (len == 0) {
      vert->nx = 0;
      vert->ny = 0;
      vert->nz = 0;
    }
    else {
      if (flip_sign)
        recip = -1 / sqrt (len);
      else
        recip = 1 / sqrt (len);
      vert->nx *= recip;
      vert->ny *= recip;
      vert->nz *= recip;
    }
  }
}


